
#include "common.h"

static int depth = 0;
GNode *bookmark_root;

static GNode *cnode;
static GNode *pnode;

enum {
    INIT,
    TOP,
    FOLDER,
    ENTRY
};

enum {
    ENTRY_HOST,
    ENTRY_PORT,
    ENTRY_PROTO,
    ENTRY_USER,
    ENTRY_PASS,
    ENTRY_UNKNOWN
};

static const char *entry_fields[] = {
    "hostname",
    "port",
    "protocol",
    "username",
    "password",
    NULL
};


static int state;
static int field;

static int get_entry_field_index (const char *name) 
{
    int i = 0;
    while (entry_fields[i]) {
        if (g_strcmp0 (name, entry_fields[i]) == 0)
            return i;
        i++;
    }

    return ENTRY_UNKNOWN;
}

static void on_enter_toplevel ()
{
    state = TOP;
    
    bookmark_root = g_node_new (NULL);
    cnode = bookmark_root;
}

static void on_leave_toplevel ()
{
    /* nothing to do */
}

static void on_enter_folder (const gchar        *element_name,
                             const gchar        **attribute_names,
                             const gchar        **attribute_values)
{
    gftp2_bookmark *bm;
    GNode *nnode;
    int i;

    state = FOLDER;

    bm = g_malloc0 (sizeof (gftp2_bookmark));
    bm->is_folder = 1;

    i = 0;
    while (attribute_names[i] != NULL)
    {
        if (g_strcmp0 (attribute_names[i], "name") == 0)
            bm->name = g_strdup (attribute_values[i]);
        else
            g_debug ("on_enter_folder: unknown attribute %s", attribute_names[i]);
        i++;
    }

    nnode = g_node_new (bm);
    g_node_insert (cnode, -1, nnode); 
    cnode = nnode;
}

static void on_leave_folder ()
{
    cnode = cnode->parent;
    gftp2_bookmark *bm = (gftp2_bookmark *)cnode->data;

    if (bm == NULL)
        state = TOP;
    else if (bm->is_folder)
        state = FOLDER;
    else
        g_error ("on_leave_folder error");
}

static void on_enter_entry  (const gchar         *element_name,
                             const gchar        **attribute_names,
                             const gchar        **attribute_values)
{
    gftp2_bookmark *bm;
    GNode *nnode;
    int i;

    state = ENTRY;
    field = -1;

    bm = g_malloc0 (sizeof (gftp2_bookmark));

    i = 0;
    while (attribute_names[i] != NULL)
    {
        if (g_strcmp0 (attribute_names[i], "name") == 0)
            bm->name = g_strdup (attribute_values[i]);
        else
            g_debug ("on_enter_folder: unknown attribute %s", attribute_names[i]);
        
        i++;
    }

    nnode = g_node_new (bm);
    g_node_insert (cnode, -1, nnode);
    cnode = nnode;
}

static void on_leave_entry ()
{
    cnode = cnode->parent;
    gftp2_bookmark *bm = (gftp2_bookmark *)cnode->data;

    if (bm == NULL)
        state = TOP;
    else if (bm->is_folder)
        state = FOLDER;
    else
        g_error ("on_leave_entry error");
}


static void
start_element_handler  (GMarkupParseContext *context,
                        const gchar         *element_name,
                        const gchar        **attribute_names,
                        const gchar        **attribute_values,
                        gpointer             user_data,
                        GError             **error)
{
    int i;

    /* printf ("ELEMENT '%s'\n", element_name); */

    switch (state) {
    case INIT:
        if (g_strcmp0 (element_name, "bookmarks") != 0)
            g_warning ("Bookmarks top element is wrong: %s", element_name);
        else
            on_enter_toplevel ();
        break;
    case TOP:
        if (g_strcmp0 (element_name, "folder") == 0) {
            on_enter_folder (element_name, attribute_names, attribute_values);
        } else if (g_strcmp0 (element_name, "site") == 0) {
            on_enter_entry (element_name, attribute_names, attribute_values);
        } else {
            g_warning ("Enter unknown element: %s", element_name);
        }
        break;
    case FOLDER:
        if (g_strcmp0 (element_name, "folder") == 0) {
            on_enter_folder (element_name, attribute_names, attribute_values);
        } else if (g_strcmp0 (element_name, "site") == 0) {
            on_enter_entry (element_name, attribute_names, attribute_values);
        } else {
            g_warning ("Enter unknown element: %s", element_name);
        }
        break;
    case ENTRY:
        field = get_entry_field_index (element_name);
        if (field == ENTRY_UNKNOWN) {
            g_warning ("Enter unkown entry element: %s", element_name);
        }
        break;
    default:
        g_warning ("Unknown state");
    }

/*
    i = 0;
    while (attribute_names[i] != NULL)
    {
        printf ("%s=\"%s\"\n", attribute_names[i], attribute_values[i]);
      
        ++i;
    }
*/
}

static void
end_element_handler    (GMarkupParseContext *context,
                        const gchar         *element_name,
                        gpointer             user_data,
                        GError             **error)
{
    switch (state) {
    case TOP:
        if (g_strcmp0 (element_name, "bookmarks") != 0)
            g_warning ("Bookmarks top element is wrong: %s", element_name);
        on_leave_toplevel ();
        break;
    case FOLDER:
        on_leave_folder ();
        break;
    case ENTRY:
        if (field == -1)        /* not in any particular field */
            on_leave_entry ();
        else
            field = -1;
        break;
    default:
        g_warning ("Unknown state");
    }
    
    /* printf ("END '%s'\n", element_name); */
}

static void
text_handler                      (GMarkupParseContext *context,
                                   const gchar         *text,
                                   gsize                text_len,
                                   gpointer             user_data,
                                   GError             **error)
{
    /* printf ("TEXT '%.*s'\n", (int)text_len, text); */
    gchar *str;
    gchar *sstr;
    gftp2_bookmark *bm;

    if (state != ENTRY)
        return;

    if (field == -1)
        return;

    g_assert (cnode != NULL);
    bm = (gftp2_bookmark *)cnode->data;

    str = g_strdup (text);
    sstr = g_strstrip (str);
    switch (field) {
    case ENTRY_HOST:
        bm->hostname = g_strdup (sstr);
        break;
    case ENTRY_PORT:
        bm->port = atoi (sstr);
        break;
    case ENTRY_PROTO:
        bm->protocol = g_strdup (sstr);
        break;
    case ENTRY_USER:
        bm->user = g_strdup (sstr);
        break;
    case ENTRY_PASS:
        bm->passwd = g_strdup (sstr);
        break;
    default:
        g_debug ("text_handler error: unknown field %d", field);
    }
    
    g_free (str);
}


static void
passthrough_handler    (GMarkupParseContext *context,
                        const gchar         *passthrough_text,
                        gsize                text_len,
                        gpointer             user_data,
                        GError             **error)
{
    /* printf ("PASS '%.*s'\n", (int)text_len, passthrough_text); */
}

static void
error_handler          (GMarkupParseContext *context,
                        GError              *error,
                        gpointer             user_data)
{
    fprintf (stderr, " %s\n", error->message);
}


static const GMarkupParser parser = {
    start_element_handler,
    end_element_handler,
    text_handler,
    passthrough_handler,
    error_handler
};

void 
gftp2_bookmark_read_file (const char *file)
{
    gchar *contents;
    gsize  length;
    GError *error;
    GMarkupParseContext *context;

    bookmark_root = NULL;
    
    error = NULL;
    if (!g_file_get_contents (file, &contents, &length, &error))
    {
        fprintf (stderr, "%s\n", error->message);
        g_error_free (error);
        return;
    }

    context = g_markup_parse_context_new (&parser, 0, NULL, NULL);

    if (!g_markup_parse_context_parse (context, contents, length, NULL))
    {
        g_markup_parse_context_free (context);
        return;
    }

    if (!g_markup_parse_context_end_parse (context, NULL))
    {
        g_markup_parse_context_free (context);
        return;
    }
    
}

static void ident (FILE *fp, int depth)
{
    int i;
    for (i=0; i<depth; i++) {
        fprintf (fp, "  ");
    }
}

static void save_file (FILE *fp, GNode *cnode, int depth)
{
    gftp2_bookmark *bm;
    
    bm = (gftp2_bookmark *)cnode->data;
    GNode *child;

    
    if (bm == NULL) {
        /* top level */
        fprintf (fp, "<bookmarks>\n");

        child = cnode->children;
        while (child != NULL) {
            save_file (fp, child, depth+1);
            child = child->next;
        }
        fprintf (fp, "</bookmarks>\n");
        return;
    }

    if (bm->is_folder) {
        ident (fp, depth);
        fprintf (fp, "<folder name=\"%s\">\n", bm->name);
        
        child = cnode->children;
        while (child != NULL) {
            save_file (fp, child, depth+1);
            child = child->next;
        }

        ident (fp, depth);
        fprintf (fp, "</folder>\n");
    } else {
        ident (fp, depth);
        fprintf (fp, "<site name=\"%s\">\n", bm->name);
        
        if (bm->hostname) {
            ident (fp, depth+1);
            fprintf (fp, "<hostname>%s</hostname>\n", bm->hostname);
        }

        ident (fp, depth+1);
        fprintf (fp, "<port>%d</port>\n", bm->port);

        ident (fp, depth+1);
        fprintf (fp, "<protocol>%s</protocol>\n", bm->protocol);
        
        if (bm->user) {
            ident (fp, depth+1);
            fprintf (fp, "<username>%s</username>\n", bm->user);
        }

        if (bm->passwd) {
            ident (fp, depth+1);
            fprintf (fp, "<password>%s</password>\n", bm->passwd);
        }

        ident (fp, depth);
        fprintf (fp, "</site>\n");
    }    
}

void gftp2_bookmark_save_file (const char *file)
{
    FILE *fp = fopen (file, "w+");
    if (fp == NULL) {
        perror ("gftp2_bookmark_save_file failed");
        return;
    }

    save_file (fp, bookmark_root, 0);

    fclose (fp);
}

void
gftp2_bookmark_save ()
{
    char *file;

    file = gftp_expand_path (GFTP2_BOOKMAKR_FILE);
    gftp2_bookmark_save_file (file);
}

void 
gftp2_bookmark_load ()
{
    char *file;
    char *tempstr;

    file = gftp_expand_path (GFTP2_BOOKMAKR_FILE);
    
    if (access (file, F_OK) == -1) {        
        tempstr = g_strdup_printf ("%s/bookmarks.xml", PACKAGE_DATA_DIR);

        if (access (tempstr, F_OK) == -1) {
            fprintf (stderr, _("gftp2 Error: Cannot find sample bookmark file %s\n"),
                    tempstr);
            fprintf (stderr, _("Did you do a make install?\n"));
            exit (EXIT_FAILURE); 
        }

        copyfile (tempstr, file);
    }

    gftp2_bookmark_read_file (file);
}

gftp2_bookmark*
gftp2_bookmark_new ()
{
    gftp2_bookmark *b;

    b = g_new0 (gftp2_bookmark, 1);
    return b;
}

/*
int main (int argc, char **argv)
{
    gftp2_bookmark_read_file (GFTP2_BOOKMAKR_FILE);
    gftp2_bookmark_save_file ("bookmark2s.xml");

    return 0;
}
*/
